<?php

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of BAddress
 *
 * @author jianfeng
 */
class BAddress extends BEntity {

    const from_all_space = 'from_all_space';
    const from_all_app = 'from_all_app';
    const from_all_operation = 'from_all_operation';
    const from_all_role = 'from_all_role';
    const from_all_user = 'from_all_user';
    const to_all_space = 'to_all_space';
    const to_all_app = 'to_all_app';
    const to_all_operation = 'to_all_operation';
    const to_all_role = 'to_all_role';
    const to_all_user = 'to_all_user';

    //put your code here
    protected $addressItems = array();
    protected $spaceList = array();
    protected $appList = array();
    protected $operationList = array();
    protected $userList = array();
    protected $roleList = array();
    protected $messageList = array();
    public $theApp = null;
    public $theSpace = null;
    public $name;

    /**
     * 
     * @param type $address
     * @return BAddress
     */
    public static function buildFromArray($address, $addressObj = NULL) {
        if (!is_array($address) || $address == NULL)
            return;
        if ($addressObj == NULL)
            $addressObj = new BAddress();
        foreach ($address as $key => $value) {
            if (strtolower($key) == 'space') {
                foreach ($value as $item) {
                    if (is_numeric($item)) {
                        if ($item <= 0) { //means support all space
                            $addressObj->spaceList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $addressObj->spaceList[$item] = $item;
                    }
                    if (is_string($item)) {
                        if (trim($item) == 'this')
                            $space = Blyn::app()->getActiveSpace();
                        elseif ($item == 'all' || $item == '*') {
                            $addressObj->spaceList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $space = BSpace::find($item);
                        if ($space instanceof BSpace) {
                            $addressObj->theSpace = $space;
                            $spaceId = $space->getId();
                            $addressObj->spaceList[$spaceId] = $spaceId;
                        } else {
                            //support string in list
                            $addressObj->spaceList[$item] = $item;
                        }
                    }
                }
            }

            if (strtolower($key) == 'app') {
                foreach ($value as $item) {
                    if (is_numeric($item)) {
                        if ($item <= 0) { //means support all space
                            $addressObj->appList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $addressObj->appList[$item] = $item;
                    }
                    if (is_string($item)) {
                        if (trim($item) == 'this')
                            $app = Blyn::app()->getActiveApp();
                        elseif ($item == 'all' || $item == '*') {
                            $addressObj->appList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $app = BApp::find($item);
                        if ($app instanceof BApp) {
                            $addressObj->theApp = $app;
                            $appId = $app->getId();
                            $addressObj->appList[$appId] = $appId;
                        } else {
                            //support string in list
                            $addressObj->appList[$item] = $item;
                        }
                    }
                }
            }

            if (strtolower($key) == 'operation') {
                if (is_string($value)) {
                    $oName = $value;
                    $value = array();
                    array_push($value, $oName);
                }
                foreach ($value as $appName => $item) {
                    if (is_numeric($item)) {
                        if ($item <= 0) { //means support all space
                            $addressObj->operationList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $addressObj->operationList[$item] = $item;
                    }
                    //the way to define operation, format like:
                    /* 1)'operation'=>array('appName'=>'operationName'...
                     * 2)'operation'=>array('appName'=>array('operationName'
                     * 'operationId'...
                     * 3)'operation'=>array('appName:operationName',....
                     * 
                     */
                    if (is_string($item)) {
                        if (trim($item) == 'this')
                            $operation = Blyn::app()->getActiveOperation();
                        elseif ($item == 'all' || $item == '*') {
                            $addressObj->operationList = array();
                            break; //means all space supported, so set spacelist as null
                        } else {
                            $theApp = null;
                            $opData = explode(':', $item);
                            if (count($opData) > 1) {
                                $appName = $opData[0];
                                $opName = $opData[1];
                            }

                            if (is_string($appName)) {
                                $theApp = BApp::find($appName);
                            }
                            //use populate app
                            if ($addressObj->theApp && $theApp == NULL) {
                                $theApp = $addressObj->theApp;
                            }
                            if (!$theApp)
                                $theApp = Blyn::app()->getActiveApp();

                            if (isset($opName) && $opName != NULL)
                                $operation = $theApp->getOperation($opName);
                        }

                        if (!$operation)
                            $operation = Blyn::app()->getActiveOperation();
                        if ($operation instanceof BOperation) {
                            $operationId = $operation->getId();
                            $addressObj->operationList[$operationId] = $operationId;
                        } else {
                            //support string in list
                            $addressObj->operationList[$item] = $item;
                        }
                    }
                }
            }

            if (strtolower($key) == 'user') {
                foreach ($value as $item) {
                    if (is_numeric($item)) {
                        if ($item <= 0) { //means support all space
                            $addressObj->userList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $addressObj->userList[$item] = $item;
                    }
                    if (is_string($item)) {
                        if (trim($item) == 'this' || trim($item) == 'me')
                            $user = Blyn::app()->getCurrentUser();
                        elseif ($item == 'all' || $item == '*') {
                            $addressObj->userList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $user = BUser::find($item);
                        if ($user instanceof BUser) {
                            $userId = $user->getId();
                            $addressObj->userList[$userId] = $userId;
                        } else {
                            //support string in list
                            $addressObj->userList[$item] = $item;
                        }
                    }
                }
            }

            if (strtolower($key) == 'role') {
                foreach ($value as $item) {
                    if (is_numeric($item)) {
                        if ($item <= 0) { //means support all space
                            $addressObj->roleList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $addressObj->roleList[$item] = $item;
                    }
                    if (is_string($item)) {
                        if ($item == 'all' || $item == '*') {
                            $addressObj->appList = array();
                            break; //means all space supported, so set spacelist as null
                        } else
                            $role = BRole::find($item);
                        if ($role instanceof BRole) {
                            $roleId = $role->getId();
                            $addressObj->roleList[$roleId] = $roleId;
                        } else {
                            //support string in list
                            $addressObj->roleList[$item] = $item;
                        }
                    }
                }
            }

            if (strtolower($key) == 'message') {
                if (is_string($value)) {
                    $addressObj->messageList[$value] = $value;
                }
                if (is_array($value)) {
                    foreach ($value as $item) {
                        if (is_string($item)) {
                            $addressObj->messageList[$item] = $item;
                        }
                    }
                }
            }
        }

        return $addressObj;
    }

    /**
     * 
     * @param type $address, format of address
     * u(u1,u2..)ug(ug1,ug2...)a(app1,app2...)s(space1,space2,...)
     * each address seperate by ; or ,
     * u = user, ug = user group, a  = app, s = space, ag = app group, sg = space group
     * r = role, c = circle, o = operation, og = operation group, of = operation flow, ofg = operation flow group,
     * msg = message
     */
    public static function buildFromString($address, $addressObj = NULL) {
        if (!is_string($address))
            return FALSE;
        $noScopeDelimiter = '/[\{\[\(](.*?)[\}\]\)]/';
        $spaceDelimiter = "/s[\{\[\(](.*?)[\}\]\)]/";
        $appDelimiter = '/a[\{\[\(](.*?)[\}\]\)]/';
        $operationDelimiter = '/o[\{\[\(](.*?)[\}\]\)]/';
        $userDelimiter = '/u[\{\[\(](.*?)[\}\]\)]/';
        $roleDelimiter = '/r[\{\[\(](.*?)[\}\]\)]/';
        $messageDelimiter = '/m[\{\[\(](.*?)[\}\]\)]/';
        $spliter = '/[,;]/';

        $addressList = array();
        $matches = array();

        //if no scope, like: a@c.d,b@d.c,....
        $userString = NULL;
        if (!preg_match($noScopeDelimiter, $address)) {
            $userString = $address;
            $spaceString = $address;
            $appString = $address;
            $userList = preg_split($spliter, $userString);
            $spaceList = preg_split($spliter, $spaceString);
            $appList = preg_split($spliter, $appString);
            if ($userList != NULL) {
                $addressList['user'] = $userList;
                //return $addressList;
            }
            if ($spaceList != NULL) {
                $addressList['space'] = $spaceList;
                //return $addressList;
            }
            if ($appList != NULL) {
                $addressList['app'] = $appList;
                //return $addressList;
            }
        }


        $userString = preg_match($userDelimiter, $address, $matches);
        if ($matches != NULL) {
            $userString = $matches[1];
            $matches = array();
        }

        if ($userString != NULL) {
            $userList = preg_split($spliter, $userString);
            if ($userList != NULL) {
                $addressList['user'] = $userList;
            }
        }

        $roleString = preg_match($roleDelimiter, $address, $matches);
        if ($matches != NULL) {
            $roleString = $matches[1];
            $matches = array();
        }

        if ($roleString != NULL) {
            $roleList = preg_split($spliter, $roleString);
            if ($roleList != NULL) {
                $addressList['role'] = $roleList;
            }
        }

        preg_match($spaceDelimiter, $address, $matches);
        if ($matches != NULL) {
            $spaceString = $matches[1];
            $matches = array();
            $spaceList = preg_split($spliter, $spaceString);
            if ($spaceList != NULL) {
                $addressList['space'] = $spaceList;
            }
        }

        preg_match($appDelimiter, $address, $matches);
        if ($matches != NULL) {
            $appString = $matches[1];
            $matches = array();
            $appList = preg_split($spliter, $appString);
            if ($appList != NULL) {
                $addressList['app'] = $appList;
            }
        }

        preg_match($operationDelimiter, $address, $matches);
        if ($matches != NULL) {
            $operationString = $matches[1];
            $matches = array();
            $operationList = preg_split($spliter, $operationString);
            if ($operationList != NULL) {
                $addressList['operation'] = $operationList;
            }
        }

        preg_match($messageDelimiter, $address, $matches);
        if ($matches != NULL) {
            $messageString = $matches[1];
            $matches = array();
            $messageList = preg_split($spliter, $messageString);
            if ($messageList != NULL) {
                $addressList['message'] = $operationList;
            }
        }

        return self::buildFromArray($addressList, $addressObj);
    }

    /**
     * this function will remove illegal address items according to context
     * @param type $addressContext
     */
    public function filterByContext($addressContext) {

        if (is_array($addressContext)) {
            $oAddressContext = self::buildFromArray($addressContext);
        }

        if ($oAddressContext instanceof BAddress) {
            if ($this->spaceList != NULL && $oAddressContext->spaceList != NULL) {
                foreach ($this->spaceList as $key => $spaceId) {
                    if (!in_array($spaceId, $oAddressContext->spaceList))
                        unset($this->spaceList[$key]);
                }
            }

            if ($this->appList != NULL && $oAddressContext->appList != NULL) {
                foreach ($this->appList as $key => $id) {
                    if (!in_array($id, $oAddressContext->appList))
                        unset($this->appList[$key]);
                }
            }

            if ($this->operationList != NULL && $oAddressContext->operationList != NULL) {
                foreach ($this->operationList as $key => $id) {
                    if (!in_array($id, $oAddressContext->operationList))
                        unset($this->operationList[$key]);
                }
            }

            if ($this->userList != NULL && $oAddressContext->userList != NULL) {
                foreach ($this->userList as $key => $id) {
                    if (!in_array($id, $oAddressContext->userList))
                        unset($this->userList[$key]);
                }
            }

            if ($this->roleList != NULL && $oAddressContext->roleList != NULL) {
                foreach ($this->roleList as $key => $id) {
                    if (!in_array($id, $oAddressContext->roleList))
                        unset($this->roleList[$key]);
                }
            }
        }
    }

    /**
     * this function populate all row for address, stand for address item
     * @param type $data
     * @return type
     */
    public function getAddressItems($data = NULL) {

        if ($this->addressItems == NULL || $data == 'reload') {

            $addressItems = array();
            $addressData = array();
            foreach ($this->spaceList as $key => $spaceId) {
                $addressItems['spaceId'] = $spaceId;
                array_push($addressData, $addressItems);
            }

            foreach ($this->appList as $key => $appId) {
                if (count($addressData) > 0) {
                    foreach ($addressData as &$item) {
                        $item['appId'] = $appId;
                    }
                } else {
                    $addressItems['appId'] = $appId;
                    array_push($addressData, $addressItems);
                }
            }

            foreach ($this->operationList as $key => $operationId) {
                if (count($addressData) > 0) {
                    foreach ($addressData as &$item) {
                        $item['operationId'] = $operationId;
                    }
                } else {
                    $addressItems['operationId'] = $operationId;
                    array_push($addressData, $addressItems);
                }
            }

            foreach ($this->userList as $key => $userId) {
                if (count($addressData) > 0) {
                    foreach ($addressData as &$item) {
                        $item['userId'] = $userId;
                    }
                } else {
                    $addressItems['userId'] = $userId;
                    array_push($addressData, $addressItems);
                }
            }

            foreach ($this->roleList as $key => $roleId) {
                if (count($addressData) > 0) {
                    foreach ($addressData as &$item) {
                        $item['roleId'] = $roleId;
                    }
                } else {
                    $addressItems['roleId'] = $roleId;
                    array_push($addressData, $addressItems);
                }
            }
            $this->addressItems = $addressData;
        }
        return $this->addressItems;
    }

    /**
     * first, will try to populate id from address,
     * then compare address to context, if find item fail to match
     * return false, otherwise return true
     * @param type $address
     * @param type $addressContext
     * @return boolean
     */
    public static function checkAllowAddress($address, $addressContext) {

        if (is_array($address)) {
            $address = self::buildFromArray($address);
        }

        if (is_array($addressContext))
            $addressContext = self::buildFromArray($addressContext);

        if (is_string($address))
            $address = self::buildFromString($address);

        if (is_string($addressContext))
            $addressContext = self::buildFromString($addressContext);

        if ($address instanceof BAddress && $addressContext instanceof BAddress) {

            $contextOperationList = $addressContext->operationList;
            $contextAppList = $addressContext->appList;
            $contextSpaceList = $addressContext->spaceList;
            $contextRoleList = $addressContext->roleList;
            $contextUserList = $addressContext->userList;

            $operationList = $address->operationList;
            $appList = $address->appList;
            $spaceList = $address->spaceList;
            $roleList = $address->roleList;
            $userList = $address->userList;

            if ($contextOperationList != NULL) {
                foreach ($operationList as $item) {
                    if (!in_array($item, $contextOperationList))
                        return FALSE;
                }
            }

            if ($contextUserList != NULL) {
                foreach ($userList as $item) {
                    if (!in_array($item, $contextUserList))
                        return FALSE;
                }
            }

            if ($contextRoleList != NULL) {
                foreach ($roleList as $item) {
                    if (!in_array($item, $contextRoleList))
                        return FALSE;
                }
            }

            if ($contextSpaceList != NULL) {
                foreach ($spaceList as $item) {
                    if (!in_array($item, $contextSpaceList))
                        return FALSE;
                }
            }

            if ($contextAppList != NULL) {
                foreach ($appList as $item) {
                    if (!in_array($item, $contextAppList))
                        return FALSE;
                }
            }

            return TRUE;
        }

        //if not legal address
        return FALSE;
    }

    public function save($context = null) {
        if (isset($context['addressName'])){
            $this->name = $context['addressName'];
            unset($context['addressName']);
        }
        $addressId = parent::save($context);

        $addresItems = $this->getAddressItems();
           
        $addressItemTable = new BMessageSheet('addressItem');
        foreach ($addresItems as $item) {
            $item['addressId']=$addressId;   
            $item += $context;
            $addressItemTable->addList($item);
        }

        /*
          foreach ($this->spaceList as $key => $id) {
          $item = new BAddressItem();
          $item->spaceId = $id;
          $item->addressId = $addressId;

          $item->save();
          }

          foreach ($this->appList as $key => $id) {
          $item = new BAddressItem();
          $item->appId = $id;
          $item->addressId = $addressId;

          $item->save();
          }

          foreach ($this->operationList as $key => $id) {
          $item = new BAddressItem();
          $item->operationId = $id;
          $item->addressId = $addressId;

          $item->save();
          }

          foreach ($this->userList as $key => $id) {
          $item = new BAddressItem();
          $item->userId = $id;
          $item->addressId = $addressId;

          $item->save();
          }

          foreach ($this->roleList as $key => $id) {
          $item = new BAddressItem();
          $item->roleId = $id;
          $item->addressId = $addressId;

          $item->save();
          }
         * 
         */

        return $addressId;
    }

    public function getAddressItemList() {
        $attributes = array();
        $attributes['address_id'] = $this->getId();
        $rowList = BMAddressItem::model()->findAllByAttributes($attributes);

        $result = array();
        foreach ($rowList as $row) {
            $itemId = $row->_id;
            $result[$itemId] = new BAddressItem($itemId);
        }

        return $result;
    }

    public function getOperationList($data = NULL) {
        return $this->operationList;
    }

    public function getMessageList($data = NULL) {
        return $this->messageList;
    }

}
